home *** CD-ROM | disk | FTP | other *** search
/ MACD 5 / MACD 5.bin / workbench / tools / czesc_2 / guispell-1.0 / minrexx.c < prev    next >
C/C++ Source or Header  |  1992-03-25  |  17KB  |  497 lines

  1. #error By license you aren't permitted to compile this.  Read GUISpell.doc.
  2. /*
  3.  *   This is an example of how REXX messages might be handled.  This is
  4.  *   a `minimum' example that both accepts asynchronous REXX messages and
  5.  *   can request REXX service [and can issue simple REXX commands to other
  6.  *   rexx servers -ljr].
  7.  *
  8.  *   Read this entire file!  It's short enough.
  9.  *
  10.  *   It is written in such a fashion that it can be attached to a program
  11.  *   with a minimum of fuss.  The only external symbols it makes available
  12.  *   are the [eight] functions.
  13.  *   [Note that the application must now declare, open, and close
  14.  *    RexxSysBase. -ljr]
  15.  *
  16.  *   This code is by Radical Eye Software, but it is put in the public
  17.  *   domain.  I would appreciate it if the following string was left in
  18.  *   both as a version check and as thanks from you for the use of this
  19.  *   code.
  20.  *
  21.  *   If you modify this file for your own use, don't bump the version
  22.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  23.  *   don't have fake `versions' floating around.
  24.  *
  25.  *   Simple command issuing extensions by
  26.  *   Loren J. Rittle  Thu Dec 05 01:33:46 1991
  27.  */
  28. static char *blurb =
  29. "Radical Eye MinRexx 0.4ljr (with simple command issuing extensions)";
  30.  
  31. /*
  32.  *  The application *must* declare, open (before calling any minrexx code),
  33.  *  and close (after calling any/all minrexx code) RexxSysBase. -ljr
  34.  */
  35.  
  36. /*
  37.  *   We read in our own personal little include.
  38.  */
  39. #include <string.h>
  40. #include <proto/exec.h>
  41. #include "libraries.h"
  42. #include "minrexx.h"
  43.  
  44. static int cmdcmp (char *c, char *m);
  45. static void replytoit (struct RexxMsg *msg);
  46.  
  47. /*
  48.  *   All of our local globals, hidden from sight.
  49.  */
  50. static struct MsgPort *rexxPort;/* this is *our* rexx port */
  51. static int bringerdown;        /* are we trying to shut down? */
  52. static struct rexxCommandList *globalrcl;    /* our command association list */
  53. static long stillNeedReplies;    /* how many replies are pending? */
  54. static long rexxPortBit;    /* what bit to wait on for Rexx? */
  55. static char *extension;        /* the extension for macros */
  56. static void (*userdisp) (struct RexxMsg *, struct rexxCommandList *, char *);    /* the user's dispatch function */
  57. static struct RexxMsg *oRexxMsg;/* the outstanding Rexx message */
  58.  
  59. /*
  60.  *   This is the main entry point into this code.
  61.  */
  62. long upRexxPort (char *s, struct rexxCommandList *rcl, char *exten, void(*uf)(struct RexxMsg *, struct rexxCommandList *, char *))
  63. /*
  64.  *   The first argument is the name of your port to be registered;
  65.  *   this will be used, for instance, with the `address' command of ARexx.
  66.  *   The second argument is an association list of command-name/user-data
  67.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  68.  *   structure with a NULL in the name field. The commands are case
  69.  *   sensitive.  The user-data field can contain anything appropriate,
  70.  *   perhaps a function to call or some other data.
  71.  *   The third argument is the file extension for ARexx macros invoked
  72.  *   by this program.  If you supply this argument, any `primitive' not
  73.  *   in the association list rcl will be sent out to ARexx for
  74.  *   interpretation, thus allowing macro programs to work just like
  75.  *   primitives.  If you do not want this behavior, supply a `NULL'
  76.  *   here, and those commands not understood will be replied with an
  77.  *   error value of RXERRORNOCMD.
  78.  *   The fourth argument is the user dispatch function.  This function
  79.  *   will *only* be called from rexxDisp(), either from the user calling
  80.  *   this function directly, or from dnRexxPort().  Anytime a command
  81.  *   match is found in the association list, this user-supplied function
  82.  *   will be called with two arguments---the Rexx message that was
  83.  *   received, and a pointer to the association pair.  This function
  84.  *   should return a `1' if the message was replied to by the function
  85.  *   and a `0' if the default success code of (0, 0) should be returned.
  86.  *   Note that the user function should never ReplyMsg() the message;
  87.  *   instead he should indicate the return values with replyRexxCmd();
  88.  *   otherwise we lose track of the messages that still lack replies.
  89.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  90.  *   If something goes wrong, it simply returns a `0'.  Note that this
  91.  *   function is safe to call multiple times because we check to make
  92.  *   sure we haven't opened already.  It's also a quick way to change
  93.  *   the association list or dispatch function.
  94.  */
  95. {
  96.  
  97. /*
  98.  *   Some basic error checking.
  99.  */
  100.   if (rcl == NULL || uf == NULL)
  101.     return (0L);
  102. /*
  103.  *   If we aren't open, we make sure no one else has opened a port with
  104.  *   this name already.  If that works, and the createport succeeds, we
  105.  *   fill rexxPortBit with the value to return.
  106.  *
  107.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  108.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  109.  */
  110.   if (rexxPort == NULL)
  111.     {
  112.       Forbid ();
  113.       if (FindPort (s) == NULL)
  114.     rexxPort = CreatePort (s, 0L);
  115.       Permit ();
  116.       if (rexxPort != NULL)
  117.     rexxPortBit = 1L << rexxPort->mp_SigBit;
  118.     }
  119. /*
  120.  *   Squirrel away these values for our own internal access, and return
  121.  *   the wait bit.
  122.  */
  123.   globalrcl = rcl;
  124.   extension = exten;
  125.   userdisp = uf;
  126.   return (rexxPortBit);
  127. }
  128.  
  129. /*
  130.  *   This function closes down the Rexx port.  It is always safe to
  131.  *   call, and should *definitely* be made a part of your cleanup
  132.  *   routine.  No arguments and no return.  It removes the Rexx port,
  133.  *   replies to all of the messages and insures that we get replies
  134.  *   to all the ones we sent out, closes the Rexx library, deletes the
  135.  *   port, clears a few flags, and leaves.
  136.  */
  137. void dnRexxPort (void)
  138. {
  139.   if (rexxPort)
  140.     {
  141.       RemPort (rexxPort);
  142.       bringerdown = 1;
  143. /*
  144.  *   A message still hanging around?  We kill it off.
  145.  */
  146.       if (oRexxMsg)
  147.     {
  148.       oRexxMsg->rm_Result1 = RXERRORIMGONE;
  149.       ReplyMsg ((struct Message *)oRexxMsg);
  150.       oRexxMsg = NULL;
  151.     }
  152.       while (stillNeedReplies)
  153.     {
  154.       WaitPort (rexxPort);
  155.       dispRexxPort ();
  156.     }
  157.       DeletePort (rexxPort);
  158.       rexxPort = NULL;
  159.     }
  160.   rexxPortBit = 0;
  161. }
  162.  
  163. /*
  164.  *   Here we dispatch any REXX messages that might be outstanding.
  165.  *   This is the main routine for handling Rexx messages.
  166.  *   This function is fast if no messages are outstanding, so it's
  167.  *   pretty safe to call fairly often.
  168.  *
  169.  *   If we are bring the system down and flushing messages, we reply
  170.  *   with a pretty serious return code RXERRORIMGONE.
  171.  *
  172.  *   No arguments, no returns.
  173.  */
  174. void dispRexxPort (void)
  175. {
  176.   register struct RexxMsg *RexxMsg;
  177.   register struct rexxCommandList *rcl;
  178.   register char *p;
  179.   register int dontreply;
  180.  
  181. /*
  182.  *   If there's no rexx port, we're out of here.
  183.  */
  184.   if (rexxPort == NULL)
  185.     return;
  186. /*
  187.  *   Otherwise we have our normal loop on messages.
  188.  */
  189.   while (RexxMsg = (struct RexxMsg *) GetMsg (rexxPort))
  190.     {
  191. /*
  192.  *   If we have a reply to a message we sent, we look at the second
  193.  *   argument.  If it's set, it's a function we are supposed to call
  194.  *   so we call it.  Then, we kill the argstring and the message
  195.  *   itself, decrement the outstanding count, and attempt to close
  196.  *   down the Rexx library.  Note that this call only succeeds if
  197.  *   there are no outstanding messages.  Also, it's pretty quick, so
  198.  *   don't talk to me about efficiency.
  199.  */
  200.       if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
  201.     {
  202.       if (RexxMsg->rm_Args[1])
  203.         {
  204.           ((int (*) (struct RexxMsg *)) (RexxMsg->rm_Args[1])) (RexxMsg);
  205.         }
  206.       DeleteArgstring (RexxMsg->rm_Args[0]);
  207.       DeleteRexxMsg (RexxMsg);
  208.       stillNeedReplies--;
  209. /*
  210.  *   The default case is we got a message and we need to check it for
  211.  *   primitives.  We skip past any initial tabs or spaces and initialize
  212.  *   the return code fields.
  213.  */
  214.     }
  215.       else
  216.     {
  217.       p = (char *) RexxMsg->rm_Args[0];
  218.       while (*p > 0 && *p <= ' ')
  219.         p++;
  220.       RexxMsg->rm_Result1 = 0;
  221.       RexxMsg->rm_Result2 = 0;
  222. /*
  223.  *   If somehow the reply is already done or postponed, `dontreply' is
  224.  *   set.
  225.  */
  226.       dontreply = 0;
  227. /*
  228.  *   If the sky is falling, we just blow up and replymsg.
  229.  */
  230.       if (bringerdown)
  231.         {
  232.           RexxMsg->rm_Result1 = RXERRORIMGONE;
  233. /*
  234.  *   Otherwise we cdr down our association list, comparing commands,
  235.  *   until we get a match.  If we get a match, we call the dispatch
  236.  *   function with the appropriate arguments, and break out.
  237.  */
  238.         }
  239.       else
  240.         {
  241.           oRexxMsg = RexxMsg;
  242.           for (rcl = globalrcl; rcl->name; rcl++)
  243.         {
  244.           if (cmdcmp (rcl->name, p) == 0)
  245.             {
  246.               if (p[strlen (rcl->name)])
  247.             userdisp (RexxMsg, rcl, p + strlen (rcl->name) + 1);
  248.               else
  249.             userdisp (RexxMsg, rcl, p + strlen (rcl->name));
  250.               break;
  251.             }
  252.         }
  253. /*
  254.  *   If we broke out, rcl will point to the command we executed; if we
  255.  *   are at the end of the list, we didn't understand the command.  In
  256.  *   this case, if we were supplied an extension in upRexxPort, we know
  257.  *   that we should send the command out, so we do so, synchronously.
  258.  *   The synchronous send takes care of our reply.  If we were given a
  259.  *   NULL extension, we bitch that the command didn't make sense to us.
  260.  */
  261.           if (rcl->name == NULL)
  262.         {
  263.           if (extension)
  264.             {
  265.               syncRexxCmd (RexxMsg->rm_Args[0], RexxMsg);
  266.               dontreply = 1;
  267.             }
  268.           else
  269.             {
  270.               RexxMsg->rm_Result1 = RXERRORNOCMD;
  271.             }
  272.         }
  273.         }
  274. /*
  275.  *   Finally, reply if appropriate.
  276.  */
  277.       oRexxMsg = NULL;
  278.       if (!dontreply)
  279.         ReplyMsg ((struct Message *)RexxMsg);
  280.     }
  281.     }
  282. }
  283.  
  284. /*
  285.  *   This is the function we use to see if the command matches
  286.  *   the command string.  Not case sensitive.  Make sure all commands
  287.  *   are given in lower case!
  288.  */
  289. static int cmdcmp (char *c, char *m)
  290. {
  291.   while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z'))))
  292.     {
  293.       c++;
  294.       m++;
  295.     }
  296.   if (!(*c) && *m)
  297.     return ((int) *m != ' ');
  298.   return ((int) *c);
  299. }
  300.  
  301. /*
  302.  *   This is the general ARexx command interface, but is not the one
  303.  *   you will use most of the time; ones defined later are easier to
  304.  *   understand and use.  But they all go through here.
  305.  */
  306. struct RexxMsg *sendRexxCmd (char *s, void (*f)(struct RexxMsg *), STRPTR p1, STRPTR p2, STRPTR p3)
  307. /*
  308.  *   The first parameter is the command to send to Rexx.
  309.  *   The second parameter is either NULL, indicating that the command
  310.  *   should execute asynchronously, or a function to be called when the
  311.  *   message we build up and send out here finally returns.  Please note
  312.  *   that the function supplied here could be called during cleanup after
  313.  *   a fatal error, so make sure it is `safe'.  This function always is
  314.  *   passed one argument, the RexxMsg that is being replied.
  315.  *   These are up to three arguments to be stuffed into the RexxMsg we
  316.  *   are building up, making the values available when the message is
  317.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  318.  */
  319. {
  320.   register struct MsgPort *rexxport;
  321.   register struct RexxMsg *RexxMsg;
  322.  
  323. /*
  324.  *   If we have too many replies out there, we just return failure.
  325.  *   Note that you should check the return code to make sure your
  326.  *   message got out!  Then, we forbid, and make sure that:
  327.  *      - we have a rexx port open
  328.  *      - Rexx is out there
  329.  *      - the library is open
  330.  *      - we can create a message
  331.  *      - we can create an argstring
  332.  *
  333.  *   If all of these succeed, we stuff a few values and send the
  334.  *   message, permit, and return.
  335.  */
  336.   if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING - 1)
  337.     return (NULL);
  338.   if ((RexxMsg = CreateRexxMsg (rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  339.       (RexxMsg->rm_Args[0] = CreateArgstring (s, (long) strlen (s))))
  340.     {
  341.       RexxMsg->rm_Action = RXCOMM;
  342.       RexxMsg->rm_Args[1] = (STRPTR) f;
  343.       RexxMsg->rm_Args[2] = p1;
  344.       RexxMsg->rm_Args[3] = p2;
  345.       RexxMsg->rm_Args[4] = p3;
  346.       RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR;
  347.       Forbid ();
  348.       if (rexxport = FindPort (RXSDIR))
  349.     PutMsg (rexxport, (struct Message *)RexxMsg);
  350.       Permit ();
  351.       if (rexxport)
  352.     {
  353.       stillNeedReplies++;
  354.       return (RexxMsg);
  355.     }
  356.       else
  357.     DeleteArgstring (RexxMsg->rm_Args[0]);
  358.     }
  359.   if (RexxMsg)
  360.     DeleteRexxMsg (RexxMsg);
  361.   return (NULL);
  362. }
  363.  
  364. /*
  365.  *   This function is used to send out an ARexx message and return
  366.  *   immediately.  Its single parameter is the command to send.
  367.  */
  368. struct RexxMsg *asyncRexxCmd (char *s)
  369. {
  370.   return (sendRexxCmd (s, NULL, NULL, NULL, NULL));
  371. }
  372.  
  373. /*
  374.  *   This function sets things up to reply to the message that caused
  375.  *   it when we get a reply to the message we are sending out here.
  376.  *   But first the function we pass in, which actually handles the reply.
  377.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  378.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  379.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  380.  *   along.
  381.  */
  382. static void replytoit (struct RexxMsg *msg)
  383. {
  384.   register struct RexxMsg *omsg;
  385.  
  386.   omsg = (struct RexxMsg *) (msg->rm_Args[2]);
  387.   replyRexxCmd (omsg, msg->rm_Result1, msg->rm_Result2, NULL);
  388.   ReplyMsg ((struct Message *)omsg);
  389. }
  390.  
  391. /*
  392.  *   This function makes use of everything we've put together so far,
  393.  *   and functions as a synchronous Rexx call; as soon as the macro
  394.  *   invoked here returns, we reply to `msg', passing the return codes
  395.  *   back.
  396.  */
  397. struct RexxMsg *syncRexxCmd (char *s, struct RexxMsg *msg)
  398. {
  399.   return (sendRexxCmd (s, &replytoit, (STRPTR)msg, NULL, NULL));
  400. }
  401.  
  402. /*
  403.  *   There are times when you want to pass back return codes or a
  404.  *   return string; call this function when you want to do that,
  405.  *   and return `1' from the user dispatch function so the main
  406.  *   event loop doesn't reply (because we reply here.)  This function
  407.  *   always returns 1.
  408.  */
  409. void replyRexxCmd (struct RexxMsg *msg, long primary, long secondary, char *string)
  410. /*
  411.  *   The first parameter is the message we are replying to.
  412.  *   The next two parameters are the primary and secondary return
  413.  *   codes.
  414.  *   The final parameter is a return string.  This string is only
  415.  *   returned if the primary return code is 0, and a string was
  416.  *   requested.
  417.  *
  418.  *   We also note that we have replied to the message that came in.
  419.  */
  420. {
  421. /*
  422.  *   Note how we make sure the Rexx Library is open before calling
  423.  *   CreateArgstring . . . and we close it down at the end, if possible.
  424.  */
  425.   if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT)))
  426.     {
  427.       if (string)
  428.     secondary = (long) CreateArgstring (string, (long) strlen (string));
  429.       else
  430.     secondary = 0L;
  431.     }
  432.   msg->rm_Result1 = primary;
  433.   msg->rm_Result2 = secondary;
  434. }
  435.  
  436. /*
  437.  *   This is the general simple command interface. Added by Loren J. Rittle
  438.  */
  439. struct RexxMsg *sendSimpleCmd (char *s, char *host, void (*f)(struct RexxMsg *), STRPTR p1, STRPTR p2, STRPTR p3)
  440. /*
  441.  *   The first parameter is the command to send to the host.
  442.  *   The second parameter is the name of the host to send to.
  443.  *   The thrid parameter is either NULL, indicating that the command
  444.  *   should execute asynchronously, or a function to be called when the
  445.  *   message we build up and send out here finally returns.  Please note
  446.  *   that the function supplied here could be called during cleanup after
  447.  *   a fatal error, so make sure it is `safe'.  This function always is
  448.  *   passed one argument, the RexxMsg that is being replied.
  449.  *   These are up to three arguments to be stuffed into the RexxMsg we
  450.  *   are building up, making the values available when the message is
  451.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  452.  */
  453. {
  454.   register struct MsgPort *rexxport;
  455.   register struct RexxMsg *RexxMsg;
  456.  
  457. /*
  458.  *   If we have too many replies out there, we just return failure.
  459.  *   Note that you should check the return code to make sure your
  460.  *   message got out!  Then, we forbid, and make sure that:
  461.  *      - we have a rexx port open
  462.  *      - Rexx is out there
  463.  *      - the library is open
  464.  *      - we can create a message
  465.  *      - we can create an argstring
  466.  *
  467.  *   If all of these succeed, we stuff a few values and send the
  468.  *   message, permit, and return.
  469.  */
  470.   if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING - 1)
  471.     return (NULL);
  472.   if ((RexxMsg = CreateRexxMsg (rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  473.       (RexxMsg->rm_Args[0] = CreateArgstring (s, (long) strlen (s))))
  474.     {
  475.       RexxMsg->rm_Action = RXCOMM | RXFF_NOIO | RXFF_RESULT | RXFF_STRING;
  476.       RexxMsg->rm_Args[1] = (STRPTR) f;
  477.       RexxMsg->rm_Args[2] = p1;
  478.       RexxMsg->rm_Args[3] = p2;
  479.       RexxMsg->rm_Args[4] = p3;
  480.       RexxMsg->rm_Node.mn_Node.ln_Name = host;
  481.       Forbid ();
  482.       if (rexxport = FindPort (host))
  483.     PutMsg (rexxport, (struct Message *)RexxMsg);
  484.       Permit ();
  485.       if (rexxport)
  486.     {
  487.       stillNeedReplies++;
  488.       return (RexxMsg);
  489.     }
  490.       else
  491.     DeleteArgstring (RexxMsg->rm_Args[0]);
  492.     }
  493.   if (RexxMsg)
  494.     DeleteRexxMsg (RexxMsg);
  495.   return (NULL);
  496. }
  497.